import { math } from "cc"
import { MAP_HEIGHT, MAP_WIDTH, MAX_SHAPE_SIZE, SHAPES } from "../define/Const"

const heightOffset = 2 //高度差为2则斜向掉落
const byteLength = MAP_WIDTH * MAP_HEIGHT
const data = new Uint8Array(byteLength)
const visited = new Uint8Array(byteLength);
const DIR8 = [-MAP_WIDTH - 1, -MAP_WIDTH, -MAP_WIDTH + 1, -1, 1, MAP_WIDTH - 1, MAP_WIDTH, MAP_WIDTH + 1];
const step: {h: number, w: number, hIndex: number, color: number}[] = []

const dirtyData: number[] = []
let needSimulate = false

export function genShapeData(shapeID: number, bytes: Uint8Array, colorIndex?: number) {
    const cfg = SHAPES[shapeID - 1]
    const maxShapeWidth = MAX_SHAPE_SIZE.width
    const leftTop = {x : (maxShapeWidth - cfg.width) / 2, y: (MAX_SHAPE_SIZE.height + cfg.height) / 2} 
    
    let color = colorIndex || math.randomRangeInt(1, 5)

    const offsets = cfg.offset
    const length = offsets.length
    for(let i = 0; i < length; i++) {
        const oset = offsets[i]
        const startX = oset.start + leftTop.x
        const y = leftTop.y - i
        for (let j = 0; j < oset.length; j++) {
            const index = y * maxShapeWidth + startX + j
            const subColor = math.randomRangeInt(0, 4)
            bytes[index] = color * 10 + subColor
        }
    }
    console.log(bytes,'bytes')
    // debugger
}

export function isShapeCanPut(shapeID: number, pos: {x: number, y: number}) {
    const cfg = SHAPES[shapeID - 1]
    const leftTop = {x : pos.x - cfg.width / 2, y: pos.y + cfg.height / 2}
    if (leftTop.x >= 0 && leftTop.y < MAP_HEIGHT && (leftTop.x + cfg.width) < MAP_WIDTH && (leftTop.y - cfg.height) >= 0) {
        const offsets = cfg.offset
        const length = offsets.length
        for(let i = 0; i < length; i++) {
            const oset = offsets[i]
            const startX = oset.start + leftTop.x
            const y = leftTop.y - i
            for (let j = 0; j < oset.length; j++) {
                const index = y * MAP_WIDTH + startX + j
                if (data[index] > 0)
                    return false
            }
        }
        return true
    }

    return false
}

export function getShapeLandPos(shapeID: number, pos: {x: number, y: number}) {
    const cfg = SHAPES[shapeID - 1]
    const colStart = pos.x - cfg.width / 2
    const rowStart = pos.y - cfg.height / 2 - 1
    if (rowStart < 0) {
        return pos
    }

    let rowEnd = 0
    for (let i = 0; i < cfg.width; i++) {
        const col = i + colStart
        for (let j = rowStart; j >= rowEnd; j--) {
            const index = col + j * MAP_WIDTH
            if (data[index] > 0) {
                rowEnd = j + 1
                break
            }
        }
    }

    return {x: pos.x, y: rowEnd + cfg.height / 2}
}

export function assignShapeToMap(shapeID: number, pos: {x: number, y: number}, bytes: Uint8Array) {
    const cfg = SHAPES[shapeID - 1]
    const maxShapeWidth = MAX_SHAPE_SIZE.width
    const leftTopInBlock = {x : (maxShapeWidth - cfg.width) / 2, y: (MAX_SHAPE_SIZE.height + cfg.height) / 2}
    const leftTopInMap = {x : pos.x - cfg.width / 2, y: pos.y + cfg.height / 2}
    const offsets = cfg.offset
    const length = offsets.length
    for(let i = 0; i < length; i++) {
        const oset = offsets[i]
        const blockStartX = oset.start + leftTopInBlock.x
        const blockY = leftTopInBlock.y - i
        const mapStartX = oset.start + leftTopInMap.x
        const mapY = leftTopInMap.y - i
        for (let j = 0; j < oset.length; j++) {
            const blockIndex = blockY * maxShapeWidth + blockStartX + j
            const mapIndex = mapY * MAP_WIDTH + mapStartX + j
            const color = bytes[blockIndex]
            if (data[mapIndex] != 0) {
                console.error('数据有误')
                return
            }
            data[mapIndex] = color
            dirtyData.push(mapIndex)
        }
    }
    dirtyData.sort((a, b) => a - b)
    needSimulate = true
}

export function simulateAllData() {
    if (!needSimulate)
        return

    let dirtyIndex = 0
    let dirty = false
    for (let i = 0; i < dirtyData.length; i++) {
        const index = dirtyData[i]
        const row = Math.floor(index / MAP_WIDTH)
        const col = index % MAP_WIDTH
        const downIndex = col + (row - 1) * MAP_WIDTH

        const curData = data[index]
        const downData = row > 0 ? data[downIndex] : -1
        if (curData > 0) {
            if (downData == 0) { //下方是否为空
                data[downIndex] = curData
                data[index] = 0

                dirtyData[dirtyIndex++] = downIndex
                dirty = true
            }
            else if (downData > 0){
                const leftMaxHeight = findSideSand(col, row)
                const rightMaxHeight = findSideSand(col, row, false)
                if (leftMaxHeight != undefined && row - leftMaxHeight >= heightOffset) {
                    data[downIndex - 1] = curData
                    dirtyData[dirtyIndex++] = (row - 1) * MAP_WIDTH + col - 1
                    data[index] = 0
                    dirty = true
                }
                else if (rightMaxHeight != undefined && row - rightMaxHeight >= heightOffset) {
                    data[downIndex + 1] = curData
                    dirtyData[dirtyIndex++] = (row - 1) * MAP_WIDTH + col + 1
                    data[index] = 0
                    dirty = true
                }
                else {
                    dirtyData[dirtyIndex++] = index
                }
            }
            else {
                dirtyData[dirtyIndex++] = index
            }
        }
    }

    if (dirty) {
        dirtyData.length = dirtyIndex
        dirtyData.sort((a, b) => a - b)
    }
    else {
        needSimulate = false
        return scanMap()
    }
}

export function getSandData() {
    return data
}

export function logData() {
    // return
    
    let allStr = ''
    for (let row = 0; row < 40; row++) {
        let str = ''
        for (let col = 0; col < MAP_WIDTH; col++) {
            const d = data[row * MAP_WIDTH + col]
            if (d == 0) {
                str += ' '
            }
            else {
                str += d
            }
        }
        str += '\n'
        allStr = str + allStr
    }
    console.log(allStr)
}

function findSideSand(col: number, row: number, isLeft = true) {
    const offset = isLeft ? -1 : 1
    const index = col + offset
    if (index < 0 || index >= MAP_WIDTH) {
        return
    }
    else {
        for (; row >= 0; row--) {
            const d = data[row * MAP_WIDTH + index]
            if (d != 0) {
                return row
            }
        }
    }
    return row
}

function scanMap() {
    visited.fill(0);
    for (let i = 0; i < byteLength; i++) {
        if (visited[i]) continue;
        const x = i % MAP_WIDTH;
        const type = data[i];

        if (type == 0)
            continue

        const indice: number[] = [];
        const q: number[] = [i];
        visited[i] = 1;

        let left = x
        let right = x

        while (q.length) {
            const cur = q.pop()!;
            indice.push(cur);  
            const cx = cur % MAP_WIDTH;
            const cy = Math.floor(cur / MAP_WIDTH);

            left = Math.min(cx, left)
            right = Math.max(cx, right)

            for (const d of DIR8) {
                const ni = cur + d;
                if (ni < 0 || ni >= byteLength) continue;
                const nx = ni % MAP_WIDTH;
                const ny = Math.floor(ni / MAP_WIDTH);

                // 八邻域在边界外（左右越界）跳过
                if (Math.abs(nx - cx) > 1 || Math.abs(ny - cy) > 1) continue;
                if (visited[ni] || Math.abs(data[ni] - type) >= 7) continue; //not same type  11 - 4

                visited[ni] = 1;
                q.push(ni);
            }
        }

        if (right - left == MAP_WIDTH - 1) {
            return indice
        }
    }
}

export function markClearSand(indice: number[]) {
    for (let i = 0; i < indice.length; i++) {
        const index = indice[i]
        data[index] = 1
    }
}

export function clearSand(indice: number[]) {
    for (let i = 0; i < indice.length; i++) {
        const index = indice[i]
        data[index] = 0
    }
}

export function markDirtyData() {
    dirtyData.length = 0
    for (let i = 0; i < byteLength; i++) {
        if (data[i] > 0) {
            dirtyData.push(i)
        }
    }
    needSimulate = true
}